home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / mm / ccmd / ccmdmd.unx < prev    next >
Encoding:
Text File  |  1990-12-18  |  22.1 KB  |  852 lines

  1. /*
  2.  Copyright (c) 1986, 1990 by The Trustees of Columbia University in
  3.  the City of New York.  Permission is granted to any individual or
  4.  institution to use, copy, or redistribute this software so long as it
  5.  is not sold for profit, provided this copyright notice is retained.
  6.  
  7.  Author: Andrew Lowry
  8. */
  9.  
  10. /* Machine dependent code for Unix systems -- Use preprocessor
  11. ** conditionals for peculiarities of particular systems, but
  12. ** PLEASE -- Avoid nesting preprocessor conditionals!
  13. **/
  14.  
  15. #include "ccmdlib.h"
  16. #include "cmfncs.h"            /* and internal symbols */
  17.  
  18. extern int errno;
  19.  
  20.  
  21. /* cmrpjmp
  22. **
  23. ** Purpose:
  24. **   Automatic reparse handler, installed via the cmsetrp macro from
  25. **   ccmd.h.  If this handler is installed in a CSB for which a reparse
  26. **   is needed, it will perform a longjmp to restart execution at the
  27. **   point following the installing cmsetrp invocation.  This point
  28. **   should be following the call to cmini that set up parsing for
  29. **   the current command line, and before the comnd call for the first
  30. **   field in the command.
  31. **
  32. ** Input arguments: None
  33. ** Output arguments: None
  34. ** Returns: Nothing
  35. **/
  36.  
  37. jmp_buf cmrpjb;            /* global jump buffer for autoreparse */
  38.  
  39. cmrpjmp()
  40. {
  41.   longjmp(cmrpjb,1);        /* do the jump */
  42.   return(CMxNOAR);        /* if it returns, it failed */
  43. }
  44.  
  45.  
  46.  
  47. /* cmerjmp, cmerjnp
  48. **
  49. ** Purpose:
  50. **   Automatic parse error handler, much like the automatic reparse
  51. **   handler described above.  The cmseter macro should be invoked
  52. **   just prior to issuing a prompt.  When a parsing error
  53. **   subsequently occurs (that is, the parse function is about to
  54. **   return anything but CMxOK), cmperr will be called to print the
  55. **   error, and then execution will jump back to the site of the macro
  56. **   invocation.  When the automatic error handler is installed, the
  57. **   user program can ignore the codes returned by parse, since they
  58. **   will always be CMxOK.  CSB field _cmerr may be examined to see
  59. **   whether the the prior command line was terminated by an error,
  60. **   and if so, which error.
  61. **
  62. **   Note: Reparse situations will be handled by the error handler if
  63. **   no reparse handler has been installed.
  64. **
  65. **   Cmerjnp is the same as cmerjmp, except that the error message is
  66. **   not printed.
  67. **
  68. ** Input arguments: None.
  69. ** Output arguments: None.
  70. ** Returns: Nothing.
  71. **/
  72.  
  73. jmp_buf cmerjb;                /* global jump buffer */
  74.  
  75. cmerjmp(ret,str,flags)
  76. int ret;                /* code that triggered the handler */
  77. char *str;
  78. int flags;
  79. {
  80.   if (str) return(cmermsg(str,flags));
  81.   cmperr(ret,flags);                /* issue error message */
  82.   longjmp(cmerjb,1);            /* take the jump */
  83.   return(CMxNOAE);            /* failed */
  84. }
  85.  
  86. cmermsg(str,flags)
  87. char *str;                /* error msg */
  88. int flags;
  89. {
  90.   cmpemsg(str,flags);            /* issue error message */
  91.   longjmp(cmerjb,1);            /* take the jump */
  92.   return(CMxNOAE);            /* failed */
  93. }
  94.  
  95. cmerjnp(ret)
  96. int ret;                /* code that triggered the handler */
  97. {
  98.   longjmp(cmerjb,1);            /* take the jump */
  99.   return(CMxNOAE);            /* failed */
  100. }
  101.  
  102.  
  103. #if BSD
  104. /*
  105.  * if handling nonblocking I/O, and a EWOULDBLOCK error comes up
  106.  * Then jump to the point set with a cmsetbl() call.
  107.  * The user then could wait for input.
  108.  */
  109.  
  110. jmp_buf cmbljb;                /* global jump buffer */
  111.  
  112. cmbljmp(ret)
  113. int ret;                /* code that triggered the handler */
  114. {
  115.   if (errno != EWOULDBLOCK)
  116.       return(ret);
  117.   longjmp(cmbljb,1);            /* take the jump */
  118.   return(CMxNOAE);            /* failed */
  119. }
  120. #endif
  121.  
  122.  
  123. /*
  124. ** Machine-dependent IO routines...  Generally, a file descriptor is
  125. ** supplied as an argument to the calls.
  126. **/
  127.  
  128. static int autowr;            /* TRUE if automatic wrap at eol */
  129. static int li;                /* lines on screen */
  130. static char tcapent[1024];        /* complete termcap entry */
  131. static char tcarea[100];        /* decoded termcap entries */
  132. static char *nl = "\n";
  133. static char *cr = "\r";
  134. static char *cl,*ce,*up;        /* pointers to decoded entries */
  135. static char *le,*nd,*down;
  136. int outc();                /* output routine for tputs */
  137.  
  138. char *tgetstr();            /* termlib routine returns string */
  139.  
  140. /* cmgetc - get a character from the input source.  Return standard return
  141.  * code. 
  142. */
  143.  
  144. int cmgetc(c,fd)
  145. int *c;                    /* pointer where char is placed */
  146. FILE * fd;                /* input file descriptor */
  147. {
  148.   int cc;                /* value from read */
  149.  
  150.   if (cmcsb._cmoj != NULL)
  151.     cmflsh(cmcsb._cmoj);        /* flush pending output */
  152.   if (fd == NULL)            /* no file descriptor.  EOF */
  153.     return(CMxEOF);
  154.   if (fd->_cnt == 0 && (cmcsb._cmflg & CM_ITTY))
  155.     cmselect(fileno(fd));
  156.   *c = cc = getc(fd);
  157.   if (cc == EOF)
  158.     return(CMxEOF);            /* end of file */
  159.   else
  160.     return(CMxOK);            /* good read */
  161. }
  162.  
  163. /* cmputc - Output a single character to the terminal */
  164.  
  165. cmputc(c,fd)
  166. char c;                    /* char to output */
  167. FILE * fd;                /* output filedesc */
  168. {
  169.  
  170.   if (fd != NULL) {
  171.     putc(c,fd);
  172.     if (c == '\n') 
  173.       cmflsh(fd);
  174.   }
  175. }
  176.  
  177. /* cmputs - Output null-terminated string to the terminal */
  178.  
  179. cmputs(s,fd)
  180. char *s;                /* output string */
  181. FILE *fd;                /* output filedesc */
  182. {
  183.   while(*s != '\0')
  184.     cmputc(*s++,fd);
  185. }
  186.  
  187. /* cmcr - Move to the beginning of the current line */
  188.  
  189. cmcr(fd)
  190. FILE * fd;                /* output filedesc */
  191. {
  192.   cmputs(cr,fd);            /* use term specific sequence */
  193. }
  194.  
  195. /* cmnl - Output a newline sequence to the comman stream */
  196.  
  197. cmnl(fd)
  198. FILE *fd;                /* output filedesc */
  199. {
  200.   cmputs(nl,fd);            /* use term-specific sequence */
  201. }
  202.  
  203. /* cmflsh - flush output on fd */
  204. cmflsh(fd)
  205. FILE *fd;
  206. {
  207.   if (fd != NULL)
  208.     fflush(fd);
  209. }
  210.  
  211. /* cmwrap - Make sure that cursor wraps when it is required */
  212.  
  213. cmwrap(fd)
  214. FILE * fd;                /* output filedesc */
  215. {
  216.   if (!autowr)
  217.     cmnl(fd);                /* newline if not automatic */
  218. }
  219.  
  220. /* cmcls - Clear the screen.  Current IOJ value in the CSB is used for
  221. ** character output.  Only invoked if that IOJ is for a CRT terminal.
  222. ** Returns TRUE iff the operation succeeds.
  223. **/
  224.  
  225. int
  226. cmcls()
  227. {
  228.   if (cl == NULL)
  229.     return(FALSE);            /* no clear screen sequence */
  230.   else {
  231.     tputs(cl,li,outc);            /* output the clear sequence */
  232.     if (cmcsb._cmoj)
  233.     fflush(cmcsb._cmoj);
  234.     return(TRUE);
  235.   }
  236. }
  237.  
  238. /* cmceol - Clear to end of line.  Current IOJ value in the CSB is
  239. ** used for character output.  Only invoked if that IOJ is for a CRT
  240. ** terminal.  Returns TRUE iff the operation succeeds.
  241. **/
  242.  
  243. cmceol()
  244. {
  245.   if (ce == NULL)
  246.     return(FALSE);            /* no ceol sequence */
  247.   else {
  248.     tputs(ce,1,outc);            /* else do the operation */
  249.     return(TRUE);
  250.   }
  251. }
  252.  
  253. /* cmupl - Moves up on line in the display without changing column
  254. ** position.  Should not wrap to bottom of screen or cause destructive
  255. ** downward scrolling.  Current IOJ value in the CSB is used for
  256. ** character output.  Only invoked if that IOJ is for a CRT terminal.
  257. ** Returns TRUE if the operation succeeds.
  258. */
  259.  
  260. cmupl()
  261. {
  262.   if (up == NULL)
  263.     return(FALSE);            /* no upline sequence */
  264.   else {
  265.     tputs(up,1,outc);            /* else do the operation */
  266.     return(TRUE);
  267.   }
  268. }
  269.  
  270. cmdownl()
  271. {
  272.   int oldcrmod;
  273.   if (down == NULL)
  274.     return(FALSE);            /* no upline sequence */
  275.   else {
  276.     if (cmcsb._cmoj) {
  277.     oldcrmod = crmod(FALSE);    
  278.     tputs(down,1,outc);            /* else do the operation */
  279.     if (cmcsb._cmoj)
  280.         fflush(cmcsb._cmoj);
  281.     crmod(oldcrmod);
  282.     return(TRUE);
  283.     }
  284.   }
  285. }
  286.  
  287. cmleft()
  288. {
  289.   if (le == NULL)
  290.     return(FALSE);            /* no move left sequence */
  291.   else {
  292.     tputs(le,1,outc);            /* else do the operation */
  293.     cmcsb._cmcol--;
  294.     return(TRUE);
  295.   }
  296. }
  297.  
  298. cmright()
  299. {
  300.   if (nd == NULL)
  301.     return(FALSE);            /* no move right sequence */
  302.   else {
  303.     tputs(nd,1,outc);            /* else do the operation */
  304.     cmcsb._cmcol++;
  305.     return(TRUE);
  306.   }
  307. }
  308.  
  309. /* cmcpos - Returns the current column position on the display.
  310. ** We just assume the ccmd package is correct, since there's
  311. ** no facility in termlib for extracting column position, and
  312. ** the price for never knowing the cursor position is too high
  313. ** (really ugly user interface due to many blank lines).
  314. **/
  315.  
  316. int
  317. cmcpos()
  318. {
  319.   return(cmcsb._cmcol);
  320. }
  321.  
  322. /* cmflush - Flush all pending input on the input source */
  323.  
  324. cmflush(fd)
  325. FILE *fd;
  326. {
  327.   if (fd != NULL) {
  328.     if (isatty(fileno(fd)))        /* if it's a terminal */
  329. #if SYSV
  330. #define TIOCFLUSH TCFLSH
  331. #endif
  332.       ioctl(fileno(fd),TIOCFLUSH,NULL);    /* flush input and output */
  333.     fd->_cnt = 0;            /* flush stdio IOBUF. */
  334.   }
  335. }
  336.  
  337. /* cmtset - Initialize the source terminal, as currently set in the
  338. ** CSB.  If the file is a terminal, a termcap entry is obtained and
  339. ** examined to see whether or not it is a hardcopy terminal.  If not,
  340. ** various control strings are read from the termcap entry and saved
  341. ** for screen operations.  In any case, terminals are placed in cbreak
  342. ** mode without echoing, and INT and TSTP signals are caught to
  343. ** prevent the terminal remaining in a funny state upon exit, and to
  344. ** place it back into the required state upon continuation.
  345. **/
  346.  
  347. cmtset()
  348. {
  349.   int ofd, ifd;
  350.   char *areap = tcarea;            /* pointer to termcap decoding area */
  351.   char *ttype;                /* terminal type */
  352.   char *gttype();
  353.   int tret;
  354.  
  355.   if (cmcsb._cmoj != NULL)
  356.     ofd = fileno(cmcsb._cmoj);        /* input file descriptor */
  357.   if (cmcsb._cmij != NULL)
  358.     ifd = fileno(cmcsb._cmij);        /* output file designator */
  359.  
  360.   cmcsb._cmflg &= ~CM_TTY;
  361.   if (cmcsb._cmoj != NULL && isatty(ofd)) { /* check if it is a terminal */
  362.     cmcsb._cmflg |= CM_OTTY;        /* yup */
  363.     ttype = gttype(ofd);        /* get the terminal type name */
  364.     if (tcapent[0] == '\0')
  365.       tret = tgetent(tcapent,ttype);    /* get termcap entry */
  366.     else
  367.       tret = 1;
  368.     if (tret != 1) {
  369.       cmcsb._cmflg2 &= ~CM_CRT;    /* no luck... assume hardcopy */
  370.       cmcsb._cmcmx = 79;        /* use default max column */
  371.       cmcsb._cmwrp = 79;        /* and wrap column */
  372.       nl = "\n";            /* and set default newline */
  373.       cr = "\r";            /* and return sequences */
  374.     }
  375.     else if (cmcsb._cmoj != NULL) {
  376.       if (tgetflag("hc"))         /* hardcopy indicated? */
  377.     cmcsb._cmflg2 &= ~CM_CRT;    /* yup, note it */
  378.       else {
  379.     cmcsb._cmflg2 |= CM_CRT;    /* else flag a crt */
  380.       }
  381.       cmtsize();            /* setup term size & termcap vars */
  382.       cmcsb._cmwrp = cmcsb._cmcmx;    /* set up autowrap column */
  383.     }
  384.     if (cmcsb._cmij != NULL && isatty(ifd)) {
  385.       cmcsb._cmflg |= CM_ITTY;        /* yup */
  386.       raw(ifd);                /* set up the terminal properly */
  387.       intson();                /* install our interrupt handlers */
  388.     }
  389.   }
  390.   else {
  391.     if (cmcsb._cmij != NULL && isatty(ifd)) {
  392.       raw(ifd);                /* set up the terminal properly */
  393.       intson();                /* install our interrupt handlers */
  394.     }
  395.     else {
  396.       cmcsb._cmflg &= ~CM_TTY;        /* not a tty, so not a crt either */
  397.       cmcsb._cmflg2 &= ~CM_CRT;
  398.       cmcsb._cmcmx = 79;        /* and just use default width */
  399.     }
  400.   }
  401. }
  402.  
  403. /* cmtend - Clean up after prior input source */
  404.  
  405. cmtend()
  406. {
  407.   int fd;
  408.  
  409.   if (cmcsb._cmij != NULL) {
  410.     fd = fileno(cmcsb._cmij);        /* file desc to shut down */
  411.  
  412.     if (cmcsb._cmflg & CM_TTY) {
  413.       unraw(fd);            /* reset former tty params */
  414.       intsoff();            /* remove our interrupt handlers */
  415.     }
  416.   }
  417. }
  418.  
  419. /*
  420.  * cmtsize()
  421.  * set up terminal size.  try using TIOCGWINSZ if possible.  If not set,
  422.  * use termcap entry.  If ioctl() succeeds, fix up termcap entry, so inferior
  423.  * processes inherit correctly.
  424.  */
  425. cmtsize()
  426. {
  427.   int li = -1, co = -1; 
  428. #ifdef TIOCGWINSZ
  429.   struct winsize w;
  430.  
  431.   if (ioctl(fileno(cmcsb._cmoj), TIOCGWINSZ, &w) == 0) {
  432.     if (w.ws_col > 0 && cmcsb._cmcmx != w.ws_col) {
  433.       co = cmcsb._cmcmx = w.ws_col;
  434.     }
  435.     if (w.ws_row > 0 && cmcsb._cmrmx != w.ws_row) {
  436.       li = cmcsb._cmrmx = w.ws_row;
  437.     }
  438.   }
  439. #else /* TIOCGWINSZ */
  440. #ifdef TIOCGSIZE
  441.   struct ttysize t;
  442.  
  443.   if (ioctl(fileno(cmcsb._cmoj), TIOCGSIZE, &t) == 0) {
  444.     if (t.ts_col > 0 && cmcsb._cmcmx != t.ts_col) {
  445.       co = cmcsb._cmcmx = t.ts_col;
  446.     }
  447.     if (t.ts_row > 0 && cmcsb._cmrmx != t.ts_row) {
  448.       li = cmcsb._cmrmx = t.ts_row;
  449.     }
  450.   }
  451. #endif /* TIOCGSIZE */
  452. #endif /* !TIOCGWINSZ */
  453.   if (co <= 0)
  454.     cmcsb._cmcmx = tgetnum("co");    /* get col count */
  455.   if (cmcsb._cmcmx <= 0)
  456.     cmcsb._cmcmx = 79;            /* use default if not given */
  457.   else
  458.     cmcsb._cmcmx--;            /* else drop to max col position */
  459.   if (li <= 0)                /*  */
  460.     cmcsb._cmrmx = tgetnum("li");    /* get row count */
  461.   if (cmcsb._cmrmx == -1)
  462.     cmcsb._cmrmx = 24;            /* or use default if necesary */
  463.   li = --cmcsb._cmrmx;
  464.   tc_setsize(cmcsb._cmrmx+1,cmcsb._cmcmx+1);
  465. }
  466.  
  467. /*
  468.  * set the "li" and "co" entries in the termcap entry
  469.  */
  470. tc_setsize(li, co) {
  471.   char buf[1024], *p1 = NULL, *p2=NULL, *p3=NULL, *p4=NULL, *strindex();
  472.   char *index();
  473.  
  474.   p1 = strindex(tcapent,"co#");        /* find num cols */
  475.   if (p1) {
  476.     p2 = index(p1+1,':');        /* and the end of it. */
  477.   }
  478.   p3 = strindex(tcapent,"li#");        /* find num lines */
  479.   if (p3) {
  480.     p4 = index(p3+1,':');        /* and the end of it */
  481.   }
  482.   if (p3)
  483.     *p3 = '\0';
  484.   if (p1) 
  485.     *p1 = '\0';
  486.   if (p2) 
  487.     *(p2++) == '\0';
  488.   else p2 = "";
  489.   if (p4) 
  490.     *(p4++) == '\0';
  491.   else
  492.     p4 = "";
  493.                     /* build new termcap string */
  494.   sprintf(buf,"%sco#%d:%sli#%d:%s", tcapent, co, p2, li, p4);
  495.   strcpy(tcapent, buf);
  496.   setenv("TERMCAP", tcapent, TRUE);    /* put it in the environment. */
  497.   tc_setents(tcarea);
  498. }  
  499.  
  500. tc_setents(areap) 
  501. char *areap;
  502. {
  503.   if (cmcsb._cmflg2 & CM_CRT) {
  504.     cl = tgetstr("cl",&areap);        /* get clear screen sequence */
  505.     ce = tgetstr("ce",&areap);        /* and clear end-of-line */
  506.     up = tgetstr("up",&areap);        /* and upline sequence */
  507.     le = tgetstr("le",&areap);        /* move left */
  508.     nd = tgetstr("nd",&areap);        /* move right */
  509.     down = tgetstr("do",&areap);    /* move down */
  510.     if (le == NULL)
  511.         le = "\b";            /* default if not specified */
  512.   }
  513.   nl = tgetstr("nl",&areap);        /* alwasy get newline sequence */
  514.   if (nl == NULL)
  515.     nl = "\n";                /* default if not specified */
  516.   cr = tgetstr("cr",&areap);        /* get return sequence */
  517.   if (cr == NULL)
  518.     cr = "\r";                /* or set default */
  519.   autowr = tgetflag("am");        /* check for autowrap */
  520.   li = cmcsb._cmrmx;
  521. }
  522.  
  523. /*
  524.  * search for an embedded string
  525.  */
  526. char *
  527. strindex(src, str) 
  528. char *src, *str;
  529. {
  530.   char *cp = src, *cp1;
  531.  
  532.   while(cp1 = index(cp, *str)) {
  533.     if (strncmp(cp1, str, strlen(str)) == 0) {
  534.       return(cp1);
  535.     }
  536.     cp = cp1+1;
  537.   }
  538.   return(NULL);
  539. }
  540.  
  541. /* gttype - Return terminal type name for given tty file descriptor 
  542. ** Auxiliary routine for cmtset
  543. **/
  544.  
  545. static char *
  546. gttype(fd)
  547. int fd;
  548. {
  549.   char *type;                /* type name of terminal */
  550.   char *name;                /* terminal name */
  551.   char *cname;                /* controlling terminal name */
  552.   int cttyfd;                /* controlling terminal file desc */
  553.   int ctty;                /* TRUE if fd is controlling tty */
  554.   FILE *typedb;                /* stream for ttytype database */
  555.   char typelin[80];            /* line from ttytype database */
  556.   char *typecp;                /* pointer into type db entry */
  557.   extern char *ttyname(), *getenv();
  558.  
  559.   cttyfd = open("/dev/tty",O_RDWR,0);    /* open the controlling tty */
  560.   if (cttyfd < 0)
  561.     ctty = FALSE;            /* bad open - assume not ctty */
  562.   else {
  563.     name = ttyname(fd);            /* get the terminal name */
  564.     cname = ttyname(cttyfd);        /* and controlling tty name */
  565.     if (strcmp(name,cname) == 0)        /* same? */
  566.       ctty = TRUE;            /* yup, it is ctrl tty */
  567.     else
  568.       ctty = FALSE;            /* nope, some other tty */
  569.     close(cttyfd);            /* no more use for this */
  570.   }
  571.                     /*  */
  572.   if (ctty) {                /* controlling terminal? */
  573.     type = getenv("TERM");        /* yup, use environment var */
  574.     if (type != NULL)
  575.       return(type);            /* give it back if successful */
  576.   }
  577.  
  578.   name += 5;                /* skip the "/dev/" prefix */
  579.   typedb = fopen("/etc/ttytype","r");     /* open type database */
  580.   if (typedb == NULL)
  581.     return("unknown");            /* give up if bad open */
  582.   while (fgets(typelin,80,typedb) != NULL) { /* scan the database */
  583.     typecp = typelin;
  584.     while ((*typecp++) != SPACE);    /* scan for space in entry */
  585.     *(typecp-1) = NULCHAR;        /* change it to null */
  586.     
  587.     if (strcmp(name,typecp) == 0) {    /* this our entry? */
  588.       fclose(typedb);            /* yup, shut database */
  589.       return(typelin);            /* and return type name */
  590.     }
  591.   }
  592.   fclose(typedb);            /* not found... close database */
  593.   return("unknown");            /* and give default */
  594. }
  595.  
  596. /* auxiliary routines to take terminals into and out of raw mode */
  597.  
  598. #ifdef BSD
  599. static struct sgttyb ttyblk, ttysav;    /* tty parameter blocks */
  600. static struct ltchars ltc,ltcsav;    /* local special chars for new  */
  601. #endif
  602.  
  603. #if SYSV
  604. static struct termio ttyblk, ttysav;
  605. #endif
  606.  
  607. /* raw - put the terminal into raw (actually cbreak) mode, turn off
  608. ** echoing and output translations, and extract the output speed for
  609. ** the termcap library.  On BSD unix systems, literal-next processing
  610. ** is also disabled.
  611. **/
  612.  
  613. static
  614. raw(fd)
  615. int fd;
  616. {
  617. #if SYSV 
  618.   ioctl(fd, TCGETA, &ttysav);
  619.   ttyblk = ttysav;
  620.   ttyblk.c_lflag &= ~(ICANON|ECHO);
  621.   ttyblk.c_cc[0] = 003;            /* interrupt char is control-c */
  622.   ttyblk.c_cc[4] = 1;
  623.   ttyblk.c_cc[5] = 1;
  624.   ioctl(fd,TCSETAW,&ttyblk);        /* set new modes . */
  625. #endif /* SYSV */
  626.  
  627. #if BSD
  628.   extern short ospeed;            /* declared in termlib */
  629.  
  630.   ioctl(fd,TIOCGETP,&ttysav);        /* get original parameters */
  631.   ttyblk = ttysav;            /* copy into new parameter block */
  632.   ospeed = ttysav.sg_ospeed;        /* save output speed for termlib */
  633.   ttyblk.sg_flags &= ~(RAW | ECHO | LCASE); /* no echoing or xlates */
  634.   ttyblk.sg_flags |= CBREAK;        /* single character reads */
  635.   ioctl(fd,TIOCSETN,&ttyblk);        /* set params, leave typeahead */
  636.   ioctl(fd,TIOCGLTC,<c);        /* get current local special chars */
  637.   ltcsav = ltc;                /* copy it for later restore */
  638.   ltc.t_lnextc = -1;            /* disable literal-next */
  639.   ioctl(fd,TIOCSLTC,<c);        /* set the new chars in place */
  640. #endif
  641. }
  642.  
  643. /* unraw - restore the tty modes in effect before raw was performed. */
  644.  
  645. static
  646. unraw(fd)
  647. int fd;
  648. {
  649. #if SYSV
  650.   ioctl(fd,TCSETAW, &ttysav);        /* put back saved params */
  651. #endif
  652. #ifdef BSD
  653.   ioctl(fd,TIOCSETN,&ttysav);        /* put back saved params */
  654.   ioctl(fd,TIOCSLTC,<csav);        /* restore local special chars */
  655. #endif
  656. }
  657.  
  658. /* outc - aux routine to be passed to termlib routines - output one char */
  659.  
  660. PASSEDSTATIC
  661. outc(c)
  662. char c;
  663. {
  664.   FILE *fd = cmcsb._cmoj;        /* get output filedesc */
  665.  
  666.   if (fd != NULL)
  667.     putc(c,fd);                /* do the write */
  668. }
  669.  
  670. /* intson - Install our interrupt handlers for INT and STOP, so
  671. ** any terminal settings we have installed will be undone before
  672. ** the program exits.  Any handlers that are already installed
  673. ** are left in place.  Those handlers should call cmdone if
  674. ** they expect to exit with the terminal set correctly.
  675. **/
  676.  
  677. #ifdef V7                /* used improve calls in version 7 */
  678. #define signal sigsys
  679. #endif
  680. #define mask(sig)   (1 << ((sig)-1))
  681.  
  682. static
  683. intson()
  684. {
  685.   SIG sighand();            /* forward decl of our handler */
  686.   int oldmask;                /* signal mask before we play */
  687.   sigval oldhand;            /* old handler */
  688.  
  689. #ifdef SIGTSTP
  690.   oldmask = sigblock(mask(SIGINT) | mask(SIGTSTP));    /* hold these */
  691. #endif
  692.   oldhand = signal(SIGINT,sighand);    /* install our handler, get prior */
  693.   if (oldhand != SIG_DFL)        /* did they have something? */
  694.     signal(SIGINT,oldhand);        /* yup, leave it there */
  695. #ifdef SIGTSTP
  696.   oldhand = signal(SIGTSTP,sighand);    /* install ours for TSTP too */
  697.   if (oldhand != SIG_DFL)
  698.     signal(SIGTSTP,oldhand);        /* but leave theirs intact */
  699.   sigsetmask(oldmask);            /* now unblock the signals */
  700. #endif /* SIGTSTP */
  701. #ifdef SIGWINCH
  702.   oldhand = signal(SIGWINCH,sighand);    /* install ours for TSTP too */
  703.   if (oldhand != SIG_DFL)
  704.     signal(SIGWINCH,oldhand);        /* but leave theirs intact */
  705.   sigsetmask(oldmask);            /* now unblock the signals */
  706. #endif /* SIGWINCH */
  707. }
  708.  
  709.  
  710. /* intsoff - Remove our interrupt handlers.  If we remove something
  711. ** that isn't ours, put it back.
  712. **/
  713.  
  714. static
  715. intsoff()
  716. {
  717.   SIG sighand();            /* forward decl of our handler */
  718.   int oldmask;                /* sig mask prior to our diddling */
  719.   sigval oldhand;            /* prior handler for a signal */
  720.  
  721. #ifdef SIGTSTP
  722.   oldmask = sigblock(mask(SIGINT) | mask(SIGTSTP));    /* block our sigs */
  723. #endif
  724.   oldhand = signal(SIGINT,SIG_DFL);    /* remove INT handler */
  725.   if (oldhand != sighand)
  726.     signal(SIGINT,oldhand);        /* replace if not ours */
  727. #ifdef SIGTSTP
  728.   oldhand = signal(SIGTSTP,SIG_DFL);    /* remove TSTP handler */
  729.   if (oldhand != sighand)
  730.     signal(SIGTSTP,oldhand);        /* replace if not ours */
  731.   sigsetmask(oldmask);            /* replace old signal mask */
  732. #endif
  733. #ifdef SIGWINCH
  734.   oldhand = signal(SIGWINCH,SIG_DFL);    /* remove TSTP handler */
  735.   if (oldhand != sighand)
  736.     signal(SIGWINCH,oldhand);        /* replace if not ours */
  737.   sigsetmask(oldmask);            /* replace old signal mask */
  738. #endif
  739. }
  740.  
  741.  
  742. /* sighand - Handler for INT and TSTP signals.  We first fix
  743. ** the terminal to its normal settings, then remove our handler
  744. ** and generate whichever signal invoked us to get the default
  745. ** action.  If the program is continued, the terminal is adjusted
  746. ** again, and our handler is reinstalled.  (This should only happen
  747. ** with TSTP signals).
  748. **/
  749.  
  750. static SIG
  751. sighand(sig,code,scp)
  752. int sig,code;                /* sig is all we care about */
  753. struct sigcontext *scp;
  754. {
  755.   int oldmask;                /* prior interrupt mask */
  756.   long getpid();            /* pids are long */
  757.  
  758.   switch (sig) {
  759. #ifdef SIGWINCH
  760.   case SIGWINCH:
  761.     cmtsize();
  762.     break;
  763. #endif
  764.   default:
  765.     cmtend();                /* fix the terminal */
  766. #ifdef BSD
  767.     oldmask = sigsetmask(0);        /* let our signal get through */
  768. #endif
  769. #ifdef SIGTSTP
  770.     if (sig == SIGTSTP)
  771.       cmnl(stdout);            /* move to new line for looks */
  772. #endif SIGTSTP
  773.     kill(getpid(),sig);            /* get the default action */
  774.     cmtset();                /* redo the terminal if continued */
  775. #ifdef BSD
  776.     sigsetmask(oldmask);        /* set mask back to before */
  777. #endif
  778.   }
  779. }
  780.  
  781.  
  782.  
  783. /* ctty, cttycls - Ctty opens the controlling terminal and returns a
  784. ** file descriptor for it.  After the first call, it just returns the
  785. ** file descriptor opened previously.  Cttycls closes the file
  786. ** descriptor opened by ctty, after which another call to ctty will
  787. ** open another one.
  788. **/
  789.  
  790. static int ttyfd = -1;
  791.  
  792. static int
  793. ctty()
  794. {
  795.   if (ttyfd == -1)
  796.     ttyfd = open("/dev/tty",O_RDWR,0);
  797.   return(ttyfd);
  798. }
  799.  
  800. static
  801. cttycls()
  802. {
  803.   if (ttyfd != -1)
  804.     close(ttyfd);
  805.   ttyfd = -1;
  806. }
  807.  
  808. crmod(val)
  809. int val;
  810. {
  811. #ifdef TIOCSETN
  812.   struct sgttyb ttyblk;
  813.   int oldval;
  814.   int fd;
  815.  
  816.   if (cmcsb._cmoj == NULL) 
  817.       return(-1);
  818.   fd = fileno(cmcsb._cmoj);
  819.  
  820.   ioctl(fd,TIOCGETP,&ttyblk);        /* get original parameters */
  821.  
  822.   oldval = ttyblk.sg_flags & CRMOD;
  823.   if (val)
  824.       ttyblk.sg_flags |= CRMOD;        /* turn on CRMOD */
  825.   else
  826.       ttyblk.sg_flags &= ~CRMOD;    /* turn off CRMOD */
  827.   ioctl(fd,TIOCSETN,&ttyblk);        /* set params, leave typeahead */
  828.   return(oldval);
  829. #else
  830.   return (0);
  831. #endif
  832. }
  833.  
  834. cmselect(fd)
  835. int fd;
  836. {
  837. #ifdef FD_SETSIZE
  838.   fd_set rfds, efds;
  839.   int r;
  840.   
  841.   while(1) {
  842.     FD_ZERO(&rfds);
  843.     FD_ZERO(&efds);
  844.     FD_SET(fd, &rfds);
  845.     FD_SET(fd, &efds);
  846.     r = select(FD_SETSIZE, &rfds, NULL, &efds, NULL);
  847.     if (r >= 0 || errno != EINTR)
  848.     return;
  849.   }
  850. #endif
  851. }
  852.